package ch.sv7.tool.dbmanager.db.dialect;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;

import ch.sv7.tool.dbmanager.db.ColumnSchema;
import ch.sv7.tool.dbmanager.db.ColumnType;
import ch.sv7.tool.dbmanager.db.ConstraintSchema;
import ch.sv7.tool.dbmanager.db.ForeignKeySchema;
import ch.sv7.tool.dbmanager.db.IndexSchema;
import ch.sv7.tool.dbmanager.db.TableSchema;
import ch.sv7.tool.dbmanager.types.TypeInfo;
import ch.sv7.tool.dbmanager.types.TypesInfo;

/**
 * TODO: Document me!
 *
 * @author svt
 *
 */
public class DefaultDialect implements Dialect {

	protected static final String NL = "\n";
	
	private TypesInfo typesInfo;
	private Properties properties = new Properties();
	
	/**
	 * @param typesInfo
	 */
	public DefaultDialect(TypesInfo typesInfo) {
		super();
		this.typesInfo = typesInfo;
	}

	public String getName(){
		return "default";
	}

	public String getProperty(String key) {
		return properties.getProperty(key);
	}

	public void setProperty(String key, String value) {
		properties.setProperty(key, value);
	}

	public String getColumnDDL(ColumnSchema schema, boolean ignoreDefault) throws DialectException {
		StringBuffer ddl = new StringBuffer();
		ddl.append(schema.getName());
		ddl.append(" ");
		ddl.append(getColumnTypeDDL(schema.getType(), ignoreDefault));
		return ddl.toString();
	}

	public String getColumnTypeDDL(ColumnType type, boolean ignoreDefault) throws DialectException {
		StringBuffer ddl = new StringBuffer();
		String typeName = type.getTypeName();
		if (typeName == null) {
			throw new DialectException("Unable to find a type name for the jdbc type " + type.getJDBCTypeName());
		}
		TypeInfo typeInfo = getTypesInfo().getTypeInfo(typeName);
		if (typeInfo == null) {
			throw new DialectException("Unable to find type information for the type name " + typeName);
		}
		if (!typeInfo.getTypeName().equals(typeName)){
			/*
			 * Rewrite typeName if type from typesInfo different
			 */
			typeName = typeInfo.getTypeName();
		}
		ddl.append(typeName);
		String createParam = typeInfo.getCreateParams();
		if (createParam != null && createParam.trim().length() > 0) {
			if (type.getSize() != ColumnType.NO_SIZE_PROVIDED){
				ddl.append("(");
				ddl.append(type.getSize());
				if (type.getDecimalSize() != ColumnType.NO_SIZE_PROVIDED 
						&& createParam.indexOf(",") > 0){
					ddl.append(",");
					ddl.append(type.getDecimalSize());
				}
				ddl.append(")");
			}
		}
		
		if (!ignoreDefault && type.getDefaultValue() != null){
			ddl.append(" DEFAULT ");
			ddl.append(type.getDefaultValue());
		}

		if (type.isAutoIncrement()) {
			ddl.append(" IDENTITY");
		} 		
		
		if (type.isNullable()) {
			ddl.append(" NULL");
		} else {
			ddl.append(" NOT NULL");
		}
		
		return ddl.toString();
	}

	public String getAddIndexDDL(IndexSchema index) {
		StringBuffer ddl = new StringBuffer();
		if (index.isPrimaryKey()){
			ddl.append("ALTER TABLE " + index.getTableSchema().getName());
			ddl.append(" ADD CONSTRAINT " + index.getName());
			ddl.append(" PRIMARY KEY (" + index.getColumns() + ")");
		} else if (index.isConstraint() && index.isUnique()){
			ddl.append("ALTER TABLE " + index.getTableSchema().getName());
			ddl.append(" ADD CONSTRAINT " + index.getName());
			ddl.append(" UNIQUE (" + index.getColumns() + ")");
			
		} else {
			ddl.append("CREATE");
			if (index.isUnique()){
				ddl.append(" UNIQUE");
			}
			ddl.append(" INDEX " + index.getName());
			ddl.append(" ON " + index.getTableSchema().getName());
			ddl.append("(" + index.getColumns() + ")");		
		}
		ddl.append(getStatementDelimiter() + NL);
		return ddl.toString();
	}

	public String getRenameColumnDDL(ColumnSchema schema, String newName) {
		StringBuffer ddl = new StringBuffer();
		ddl.append("ALTER TABLE " + schema.getTable().getName());
		ddl.append(" RENAME COLUMN " + schema.getName() + " TO " + newName);
		ddl.append(getStatementDelimiter() + NL);
		return ddl.toString();
	}

	public String getRenameTableDDL(TableSchema schema, String newName) {
		StringBuffer ddl = new StringBuffer();
		ddl.append("ALTER TABLE " + schema.getName());
		ddl.append(" RENAME TO " + newName);
		ddl.append(getStatementDelimiter() + NL);
		return ddl.toString();
	}

	public String getStatementDelimiter() {
		return ";";
	}

	public String getTableDDL(TableSchema schema) throws DialectException {
		StringBuffer ddl = new StringBuffer();
		ddl.append("CREATE TABLE " + schema.getName() + " (" + NL);
		
		/*
		 * Iterate in columns
		 */
		ColumnSchema[] columns = schema.getColumnSchemas();
		for (int i=0; i<columns.length; i++) {
			ColumnSchema col = columns[i];
			ddl.append("   ");
			ddl.append(getColumnDDL(col, false));
			if (i< (columns.length - 1)){
				ddl.append(",");
			}
			ddl.append(NL);
		}
				
		ddl.append(") ");
		return ddl.toString();
	}
	
	public String getCompleteTableDDL(TableSchema schema, boolean includePrimaryKeys, boolean includeIndexes, boolean includeForegnKeys, boolean includeConstraints) throws DialectException {
		StringBuffer ddl = new StringBuffer();
		ddl.append(getTableDDL(schema));
		
		ddl.append(getStatementDelimiter() + NL);
		
		if (includePrimaryKeys) {
			List indexes = schema.getIndexes();
			for (int i=0; i<indexes.size(); i++) {
				IndexSchema index = (IndexSchema)indexes.get(i);
				if (index.isPrimaryKey()){
					ddl.append(getAddIndexDDL(index));
				}
			}
		}
		
		if (includeIndexes) {
			List indexes = schema.getIndexes();
			for (int i=0; i<indexes.size(); i++) {
				IndexSchema index = (IndexSchema)indexes.get(i);
				if (!index.isPrimaryKey()){
					ddl.append(getAddIndexDDL(index));
				}
			}
		}
		
		if (includeForegnKeys) {
			List fks = schema.getForeignKeys();
			for (int i=0; i<fks.size(); i++) {
				ForeignKeySchema fk = (ForeignKeySchema)fks.get(i);
				ddl.append(getAddForeignKeyDDL(fk));
			}
		}
		
		if (includeConstraints) {
			List constraints = schema.getConstraints();
			for (int i=0; i< constraints.size(); i++) {
				ConstraintSchema cs = (ConstraintSchema)constraints.get(i);
				ddl.append(getAddConstraintDDL(cs));
			}
		}
		return ddl.toString();
	}

	public String getDropTableDDL(TableSchema schema) {
		StringBuffer ddl = new StringBuffer();
		ddl.append("DROP TABLE " + schema.getName());
		ddl.append(getStatementDelimiter() + NL);
		return ddl.toString();
	}
	
	/**
	 * @return the typesInfo
	 */
	public TypesInfo getTypesInfo() {
		return typesInfo;
	}

	public String getAddColumnDDL(ColumnSchema schema) throws DialectException {
		StringBuffer ddl = new StringBuffer();
		ddl.append("ALTER TABLE " + schema.getTable().getName());
		ddl.append(" ADD ");
		ddl.append(getColumnDDL(schema, false));
		ddl.append(getStatementDelimiter() + NL);
		return ddl.toString();
	}
	
	public String getUpdateColumnDDL(ColumnSchema schema, boolean ignoreDefault) throws DialectException {
		StringBuffer ddl = new StringBuffer();
		ddl.append("ALTER TABLE " + schema.getTable().getName());
		ddl.append(" MODIFY ");
		ddl.append(getColumnDDL(schema, ignoreDefault));
		ddl.append(getStatementDelimiter() + NL);
		return ddl.toString();
	}
	
	public String getAddForeignKeyDDL(ForeignKeySchema fk) throws DialectException {
		StringBuffer ddl = new StringBuffer();
		ddl.append("ALTER TABLE " + fk.getTableSchema().getName());
		ddl.append(" ADD CONSTRAINT " + fk.getName());
		ddl.append(" FOREIGN KEY (" + fk.getColumns() + ")");
		ddl.append(" REFERENCES " + fk.getForeignTable());
		ddl.append("(" + fk.getForeignColumns() + ")");
		ddl.append(getStatementDelimiter() + NL);
		return ddl.toString();
	}
	
	public String getRenameForeignKeyDDL(ForeignKeySchema fk, String newName) throws DialectException {
		throw new DialectException("Unavailable function in this dialect");
	}

	public String getRenameIndexDDL(IndexSchema index, String newName) throws DialectException {
		throw new DialectException("Unavailable function in this dialect");
	}

	public void readDatabaseSpecificInformation(TableSchema tableSchema, Connection conn) throws DialectException {
		//throw new DialectException("Unavailable function in this dialect");	
	}

	public String getDisableAutoIncrementDDL(ColumnSchema schema) {
		return "";
	}

	public String getEnableAutoIncrementDDL(ColumnSchema schema) {
		return "";
	}

	public String getAddConstraintDDL(ConstraintSchema constraint) throws DialectException {
		StringBuffer ddl = new StringBuffer();
		ddl.append("ALTER TABLE " + constraint.getTableSchema().getName());
		ddl.append(" ADD CONSTRAINT " + constraint.getName());
		ddl.append(" CHECK (" + constraint.getDefinition() + ")");
		ddl.append(getStatementDelimiter() + NL);
		return ddl.toString();	
	}
	
	public String getDropConstraintDDL(ConstraintSchema constraint) throws DialectException {
		StringBuffer ddl = new StringBuffer();
		ddl.append("ALTER TABLE " + constraint.getTableSchema().getName());
		ddl.append(" DROP CONSTRAINT " + constraint.getName());
		ddl.append(getStatementDelimiter() + NL);
		return ddl.toString();	
	}

	public String getDropForeignKeyDDL(ForeignKeySchema fk) throws DialectException {
		StringBuffer ddl = new StringBuffer();
		ddl.append("ALTER TABLE " + fk.getTableSchema().getName());
		ddl.append(" DROP CONSTRAINT " + fk.getName());
		ddl.append(getStatementDelimiter() + NL);
		return ddl.toString();
	}

	public String getDropIndexDDL(IndexSchema index) throws DialectException {
		StringBuffer ddl = new StringBuffer();
		if (!index.isPrimaryKey() && !index.isConstraint()){
			ddl.append("DROP INDEX " 
					+ index.getTableSchema().getName() + "." + index.getName());
		} else {
			ddl.append("ALTER TABLE " + index.getTableSchema().getName());
			ddl.append(" DROP CONSTRAINT " + index.getName());
		}
		ddl.append(getStatementDelimiter() + NL);
		return ddl.toString();
	}

	
	
	public String getFixNullValuesSQL(ColumnSchema schema) throws DialectException {
		StringBuffer ddl = new StringBuffer();
		ddl.append("UPDATE " + schema.getTable().getName());
		ddl.append(" SET " + schema.getName() + " = " + schema.getType().getDefaultValue());
		ddl.append(" WHERE " + schema.getName() + " IS NULL");
		ddl.append(getStatementDelimiter() + NL);
		return ddl.toString();
	}

	public String getDefaultTypeName(String jdbcTypeName) {
		return jdbcTypeName;
	}

	public char getDataTypeConversion(String fromType, String toType) {
		return Dialect.CONVERSION_TYPE_CUSTOM;
	}

	public boolean isRealError(SQLException e) {
		return true;
	}

	public Properties getContextProperties() {
		return new Properties();
	}

}
